package com.fengwk.support.util;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;

import com.fengwk.support.code.AES;
import com.fengwk.support.code.RSA;
import com.fengwk.support.code.Snowflake;
import com.fengwk.support.constant.SupportConstant;
import com.fengwk.support.exception.BaseException;
import com.fengwk.support.util.LoggerUtils.Logger;

/**
 * 编码工具
 */
public final class CodeUtils {
	
    private static final Logger LOG = LoggerUtils.getLogger(CodeUtils.class);
    
	// 不允许改动
	private static final long SNOWFLAKE_TWEPOCH = DateUtils.toDate("2017-07-27", DateUtils.YYYYMMDD).getTime();
	
	private static final Map<String, Snowflake> SNOWFLAKE_MAP = new HashMap<String, Snowflake>();
	
	private static final String J1KJ_CODE_KEY = CodeUtils.class.getName() + ".J1KJ_CODE_KEY";
	
	private static final String J1KJ_CODE_SALT = CodeUtils.class.getName() + ".J1KJ_CODE_SALT";
	
	private static final AES J1KJ_AES = new AES(J1KJ_CODE_KEY);
	
	private static final char[] RANDOM_DICTIONARY = {
			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
			'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 
			};
	
	private CodeUtils() {
		throw new BaseException("can not instance " + getClass());
	}
	
	/**
	 * 雪花算法id[使用时限2017/7/27 + 69年]
	 * 
	 * @param workerId 0-32
	 * @param datacenterId 0-32
	 * @return
	 */
	public static long snowflake(long workerId, long datacenterId) {
		String key = workerId + "_" + datacenterId;
		Snowflake snowflake = SNOWFLAKE_MAP.get(key);
		if(snowflake == null) {
			snowflake = new Snowflake(workerId, datacenterId, SNOWFLAKE_TWEPOCH);
			SNOWFLAKE_MAP.put(key, snowflake);
		}
		return snowflake.nextId();
	}
	
	/**
	 * 获取随机码
	 * 
	 * @param count 位数
	 * @return
	 */
	public static String random(int count) {
		StringBuffer buf = new StringBuffer();
		for(int i = 0; i < count; i ++) 
			buf.append(RANDOM_DICTIONARY[MathUtils.getRandomBetweenInteger(0, RANDOM_DICTIONARY.length - 1)]);
		return buf.toString();
	}
	
	/**
	 * 获取uuid
	 * 
	 * @return
	 */
	public static String uuid() {
		return UUID.randomUUID().toString().replace("-", "");
	}
	
	/**
	 * sha1
	 * 
	 * @param data
	 * @return
	 */
	public static String sha1Hex(byte[] data) {
		return DigestUtils.sha1Hex(data);
	}
	
	/**
	 * sha1
	 * 
	 * @param content
	 * @return
	 */
	public static String sha1Hex(String content) {
		return DigestUtils.sha1Hex(content);
	}
	
	/**
	 * md5散列
	 * 
	 * @param data 加密内容
	 * @return
	 */
	public static String md5Hex(byte[] data) {
		return DigestUtils.md5Hex(data);
	}
	
	/**
	 * md5散列
	 * 
	 * @param content 加密内容
	 * @return
	 */
	public static String md5Hex(String content) {
		return md5Hex(content.getBytes());
	}
	
    /** 
     * base64编码
     * 
     * @param bytes 
     * @return 
     * @throws Exception 
     */  
    public static String base64Encode(final byte[] bytes) {  
        return new String(Base64.encodeBase64(bytes));  
    }
	
	/** 
	 * base64解码
	 * 
     * @param bytes 
     * @return 
     */
    public static byte[] base64Decode(final byte[] bytes) {  
        return Base64.decodeBase64(bytes);  
    }
    
	/** 
	 * base64解码
	 * 
     * @param bytes 
     * @return 
     */
    public static byte[] base64Decode(final String str) {  
        return Base64.decodeBase64(str);
    }
    
    /**
     * url编码
     * 
     * @param content
     * @return
     */
    public static String urlEncode(String content) {
    	try {
			return URLEncoder.encode(content, SupportConstant.DEFAULT_CHARSET);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
    	return null;
    }
    
    /**
     * url解码
     * 
     * @param content
     * @return
     */
    public static String urlDecode(String content) {
    	try {
			return URLDecoder.decode(content, SupportConstant.DEFAULT_CHARSET);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
    	return null;
    }
	
	/**
	 * 使用key来构建一个AES对称加密算法
	 * 
	 * @param key
	 * @return
	 */
	public static AES aes(String key) {
		return new AES(key);
	}
	
	/**
	 * 构建一个RSA非对称加密算法
	 * 
	 * @param keySize 秘钥bit长度>=512
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static RSA rsa(int keySize) throws NoSuchAlgorithmException {
		return new RSA(keySize);
	}
	
	/**
	 * 使用公钥和私钥构建一个RSA非对称加密算法
	 * 
	 * @param publicKeyData
	 * @param privateKeyData
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeySpecException
	 */
	public static RSA rsa(byte[] publicKeyData, byte[] privateKeyData) throws NoSuchAlgorithmException, InvalidKeySpecException {
		return new RSA(publicKeyData, privateKeyData);
	}
	
	/**
	 * 使用公钥和私钥构建一个RSA非对称加密算法
	 * 
	 * @param publicKey
	 * @param privateKey
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeySpecException
	 */
	public static RSA rsa(String publicKey, String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
		return new RSA(publicKey.getBytes(), privateKey.getBytes());
	}
	
	/**
	 * 二进制转十六进制
	 * 
	 * @param buf
	 * @return
	 */
	public static String binary2hex(byte[] binary) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < binary.length; i++) {
			String hex = Integer.toHexString(binary[i] & 0xFF);
			if (hex.length() == 1) {
				hex = '0' + hex;
			}
			sb.append(hex.toUpperCase());
		}
		return sb.toString();
	}

	/**
	 * 十六进制转二进制
	 * 
	 * @param hexStr
	 * @return
	 */
	public static byte[] hex2binary(String hexStr) {
		if (hexStr.length() < 1)
			return null;
		byte[] result = new byte[hexStr.length() / 2];
		for (int i = 0; i < hexStr.length() / 2; i++) {
			int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
			int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
			result[i] = (byte) (high * 16 + low);
		}
		return result;
	}
    
    /**
     * j1kj秘钥加密算法
     * 
     * @param content
     * @return
     */
    public static String j1kjSecretEncrypt(String content) {
        if (content == null)
            return null;
    	try {
            return J1KJ_AES.encrypt2hex(content + J1KJ_CODE_SALT);
        } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException
                | NoSuchPaddingException | UnsupportedEncodingException e) {
            LOG.error(e);
        }
    	return null;
    }
    
    /**
     * j1kj秘钥解密算法
     * 
     * @param content
     * @return
     */
    public static String j1kjSecretDecrypt(String content) {
        if (content == null)
            return null;
        try {
            content = J1KJ_AES.decrypt2str(content);
        } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException
                | NoSuchPaddingException | UnsupportedEncodingException e) {
            LOG.error(e);
        }
    	return content.replace(J1KJ_CODE_SALT, "");
    }
    
    /**
     * j1kj哈希加密算法
     * 
     * @param content
     * @return
     */
    public static String j1kjHashEncode(String content) {
    	if(content == null) 
    		return null;
        try {
            String aesStr = J1KJ_AES.encrypt2hex(content);
            return md5Hex(aesStr);
        } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException
                | NoSuchPaddingException | UnsupportedEncodingException e) {
            LOG.error(e);
        }
		return null;
    }
	
}
